#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
MCP服务
MCP Service

基于FastMCP的MCP协议服务器，提供：
- AI助手集成接口
- 异步HTTP客户端
- 工具函数封装
- 生命周期管理
"""

import logging
import asyncio
from contextlib import asynccontextmanager
from typing import Dict, Any, Optional

import httpx
from fastmcp import FastMCP

from ..config.settings import Settings

logger = logging.getLogger("MCPService")


class MCPService:
    """MCP服务类"""
    
    def __init__(self, web_service_url: str, config: Settings):
        """
        初始化MCP服务
        
        Args:
            web_service_url: Web服务的基础URL
            config: 系统配置
        """
        self.web_service_url = web_service_url.rstrip('/')
        self.config = config
        self.http_client: Optional[httpx.AsyncClient] = None
        
        # 设置关闭事件和服务器任务
        self._stop_event = asyncio.Event()
        self._server_task = None

        # 创建FastMCP实例
        self.mcp = FastMCP(
            name=config.mcp.name,
            lifespan=self._lifespan
        )

        # 注册MCP工具
        self._register_tools()

        logger.info("MCP服务初始化完成")

    def _handle_http_error(self, e: Exception, operation: str) -> Dict[str, Any]:
        """
        统一处理HTTP错误，解析服务器返回的具体错误信息

        Args:
            e: 异常对象
            operation: 操作名称

        Returns:
            格式化的错误响应
        """
        if isinstance(e, httpx.HTTPStatusError):
            try:
                # 尝试解析错误响应中的具体信息
                error_data = e.response.json()
                if error_data.get('error_type'):
                    # 如果有具体的错误类型，返回详细错误信息
                    logger.error(f"{operation}失败 - {error_data.get('error_type')}: {error_data.get('message')}")
                    return {
                        "success": False,
                        "error": error_data.get('message', str(e)),
                        "error_type": error_data.get('error_type'),
                        "error_code": error_data.get('error_code'),
                        "details": error_data.get('details', {}),
                        "http_status": e.response.status_code
                    }
                else:
                    # 返回通用错误信息
                    logger.error(f"{operation}失败 - HTTP {e.response.status_code}: {error_data.get('error', str(e))}")
                    return {
                        "success": False,
                        "error": error_data.get('error', str(e)),
                        "http_status": e.response.status_code
                    }
            except (ValueError, KeyError, AttributeError):
                # 如果无法解析响应，返回原始错误
                logger.error(f"{operation}失败 - HTTP {e.response.status_code}: {str(e)}")
                return {
                    "success": False,
                    "error": f"HTTP {e.response.status_code}: {str(e)}",
                    "http_status": e.response.status_code
                }
        elif isinstance(e, httpx.HTTPError):
            logger.error(f"{operation}失败 - HTTP错误: {e}")
            return {"success": False, "error": f"HTTP error occurred: {e}"}
        else:
            logger.error(f"{operation}失败 - 未知异常: {e}")
            return {"success": False, "error": f"An error occurred: {e}"}
    
    @asynccontextmanager
    async def _lifespan(self, app):
        """MCP应用生命周期管理"""
        # 启动时创建HTTP客户端
        self.http_client = httpx.AsyncClient(timeout=30.0)
        logger.info("MCP HTTP客户端已创建")
        
        try:
            yield
        finally:
            # 关闭时释放HTTP客户端
            if self.http_client:
                await self.http_client.aclose()
                logger.info("MCP HTTP客户端已关闭")
    
    def _register_tools(self):
        """注册MCP工具函数"""
        
        @self.mcp.tool
        async def detect_faces() -> Dict[str, Any]:
            """
            获取当前画面中检测到的人脸信息
            
            这个工具函数会返回实时的人脸检测结果，包括：
            - 检测到的人脸数量
            - 人脸边界框坐标
            - 检测结果的时间戳
            - 推理时间统计
            
            Returns:
                包含人脸检测结果的JSON对象：
                {
                    "success": true,
                    "data": {
                        "faces": [[x1, y1, x2, y2], ...],
                        "face_count": 2,
                        "inference_time": 0.045,
                        "timestamp": 1640995200.123
                    }
                }
            """
            try:
                response = await self.http_client.get(f"{self.web_service_url}/api/faces")
                response.raise_for_status()
                return response.json()
            except httpx.HTTPError as e:
                logger.error(f"获取人脸检测数据失败: {e}")
                return {"success": False, "error": f"HTTP error occurred: {e}"}
            except Exception as e:
                logger.error(f"获取人脸检测数据异常: {e}")
                return {"success": False, "error": f"An error occurred: {e}"}
        
        @self.mcp.tool
        async def control_gimbal(pan: float, tilt: float) -> Dict[str, Any]:
            """
            控制云台转动到指定的绝对角度位置
            
            这个工具函数可以精确控制二自由度云台的水平和垂直角度。
            云台的角度范围为0-270度，初始位置通常为135度（中心位置）。
            
            Args:
                pan: 水平角度，范围0-270度（0度=最左，135度=中心，270度=最右）
                tilt: 垂直角度，范围0-270度（0度=最下，135度=中心，270度=最上）
                
            Returns:
                云台控制执行结果：
                {
                    "success": true,
                    "message": "云台已移动到 Pan=90.0°, Tilt=135.0°",
                    "angles": {"pan": 90.0, "tilt": 135.0}
                }
                
            使用示例：
                - 转向左上角：control_gimbal(45, 180)
                - 回到中心位置：control_gimbal(135, 135)
                - 转向右下角：control_gimbal(225, 90)
            """
            try:
                payload = {"pan": pan, "tilt": tilt}
                response = await self.http_client.post(
                    f"{self.web_service_url}/api/gimbal/control",
                    json=payload
                )
                response.raise_for_status()
                result = response.json()
                logger.info(f"云台控制成功: {payload}")
                return result
            except Exception as e:
                return self._handle_http_error(e, "云台控制")
        
        @self.mcp.tool
        async def move_gimbal_relative(pan_delta: float, tilt_delta: float) -> Dict[str, Any]:
            """
            相对移动云台，在当前位置基础上增加指定的角度偏移
            
            这个工具函数用于微调云台位置，在当前角度基础上进行相对移动。
            正值表示增加角度，负值表示减少角度。
            
            Args:
                pan_delta: 水平角度变化量（正值向右，负值向左）
                tilt_delta: 垂直角度变化量（正值向上，负值向下）
                
            Returns:
                相对移动执行结果：
                {
                    "success": true,
                    "message": "云台相对移动 Pan=+10.0°, Tilt=-5.0°",
                    "current_angles": {"pan": 145.0, "tilt": 130.0}
                }
                
            使用示例：
                - 向右上微调：move_gimbal_relative(10, 10)
                - 向左下微调：move_gimbal_relative(-10, -10)
                - 只调整水平：move_gimbal_relative(15, 0)
            """
            try:
                payload = {"pan_delta": pan_delta, "tilt_delta": tilt_delta}
                response = await self.http_client.post(
                    f"{self.web_service_url}/api/gimbal/move_relative",
                    json=payload
                )
                response.raise_for_status()
                result = response.json()
                logger.info(f"云台相对移动成功: {payload}")
                return result
            except Exception as e:
                return self._handle_http_error(e, "云台相对移动")
        
        @self.mcp.tool
        async def start_face_tracking(target_id: int = None) -> Dict[str, Any]:
            """
            启动自动人脸追踪功能
            
            这个工具函数控制智能人脸追踪系统的开关。当启用时，系统会：
            1. 自动检测画面中的人脸
            2. 选择指定ID的人脸或最大的人脸作为追踪目标
            3. 使用PID控制算法平滑地调整云台角度
            4. 使用卡尔曼滤波器预测人脸运动轨迹
            5. 保持目标人脸在画面中心位置
            
            Args:
                target_id: 指定追踪的目标ID（可选），如果不指定则追踪最大的人脸
                
            Returns:
                追踪控制执行结果：
                {
                    "success": true,
                    "message": "人脸追踪已启动",
                    "target_id": 0
                }
                
            注意事项：
                - 启动追踪前确保画面中有人脸
                - 追踪过程中云台会自动移动
                - 可以随时停止追踪恢复手动控制
            """
            try:
                payload = {"target_id": target_id} if target_id is not None else {}
                response = await self.http_client.post(
                    f"{self.web_service_url}/api/tracking/start",
                    json=payload
                )
                response.raise_for_status()
                result = response.json()
                logger.info("人脸追踪启动成功")
                return result
            except httpx.HTTPError as e:
                logger.error(f"启动人脸追踪HTTP错误: {e}")
                return {"success": False, "error": f"HTTP error occurred: {e}"}
            except Exception as e:
                logger.error(f"启动人脸追踪异常: {e}")
                return {"success": False, "error": f"An error occurred: {e}"}
        
        @self.mcp.tool
        async def stop_face_tracking() -> Dict[str, Any]:
            """
            停止自动人脸追踪功能
            
            这个工具函数会停止当前的人脸追踪，云台将停止自动移动，
            可以切换回手动控制模式。
            
            Returns:
                停止执行结果：
                {
                    "success": true,
                    "message": "人脸追踪已停止"
                }
                
            使用场景：
                - 需要手动控制云台时
                - 追踪目标丢失时
                - 切换到其他工作模式时
            """
            try:
                response = await self.http_client.post(
                    f"{self.web_service_url}/api/tracking/stop",
                    json={}
                )
                response.raise_for_status()
                result = response.json()
                logger.info("人脸追踪停止成功")
                return result
            except httpx.HTTPError as e:
                logger.error(f"停止人脸追踪HTTP错误: {e}")
                return {"success": False, "error": f"HTTP error occurred: {e}"}
            except Exception as e:
                logger.error(f"停止人脸追踪异常: {e}")
                return {"success": False, "error": f"An error occurred: {e}"}

        @self.mcp.tool
        async def get_gimbal_status() -> Dict[str, Any]:
            """
            获取云台当前状态信息

            这个工具函数返回云台的详细状态信息，包括：
            - 当前的水平和垂直角度
            - 云台连接状态
            - 配置信息
            - 控制统计数据

            Returns:
                云台状态信息：
                {
                    "success": true,
                    "data": {
                        "connected": true,
                        "current_angles": {"pan": 135.0, "tilt": 135.0},
                        "config": {...},
                        "stats": {...}
                    }
                }
            """
            try:
                response = await self.http_client.get(f"{self.web_service_url}/api/gimbal/status")
                response.raise_for_status()
                return response.json()
            except httpx.HTTPError as e:
                logger.error(f"获取云台状态失败: {e}")
                return {"success": False, "error": f"HTTP error occurred: {e}"}
            except Exception as e:
                logger.error(f"获取云台状态异常: {e}")
                return {"success": False, "error": f"An error occurred: {e}"}

        @self.mcp.tool
        async def reset_gimbal_position() -> Dict[str, Any]:
            """
            重置云台到初始中心位置

            这个工具函数会将云台重置到预设的初始位置（通常为135°, 135°），
            这是云台的机械中心位置，提供最大的运动范围。

            Returns:
                重置执行结果：
                {
                    "success": true,
                    "message": "云台位置已重置为 Pan=135.0°, Tilt=135.0°",
                    "angles": {"pan": 135.0, "tilt": 135.0}
                }

            使用场景：
                - 系统初始化时的位置校准
                - 追踪丢失目标后的位置复位
                - 手动控制后的快速回中
            """
            try:
                response = await self.http_client.post(
                    f"{self.web_service_url}/api/gimbal/reset",
                    json={}
                )
                response.raise_for_status()
                result = response.json()
                logger.info("云台位置重置成功")
                return result
            except Exception as e:
                return self._handle_http_error(e, "云台重置")

        @self.mcp.tool
        async def get_video_stream_url() -> Dict[str, Any]:
            """
            获取实时视频流的访问地址

            这个工具函数返回MJPEG格式的实时视频流URL，可以在浏览器中直接查看。
            视频流包含：
            - 实时摄像头画面
            - 人脸检测框（绿色）
            - 追踪目标框（如果启用追踪）
            - 画面中心点标记
            - 系统状态信息显示

            Returns:
                视频流URL信息：
                {
                    "success": true,
                    "video_stream_url": "http://192.168.1.100:5000/video_feed",
                    "format": "MJPEG",
                    "resolution": "640x480",
                    "description": "实时人脸检测和追踪视频流"
                }
            """
            try:
                video_url = f"{self.web_service_url}/video_feed"
                return {
                    "success": True,
                    "video_stream_url": video_url,
                    "format": "MJPEG",
                    "resolution": f"{self.config.camera.width}x{self.config.camera.height}",
                    "description": "实时人脸检测和追踪视频流，包含检测框和追踪信息"
                }
            except Exception as e:
                return {"success": False, "error": f"获取视频流URL失败: {e}"}

        @self.mcp.tool
        async def get_system_status() -> Dict[str, Any]:
            """
            获取系统健康状态和运行信息

            这个工具函数提供系统的全面状态检查，包括：
            - 摄像头连接状态
            - 云台控制器状态
            - 人脸检测系统状态
            - 自动追踪功能状态
            - 系统性能统计
            - 应用信息

            Returns:
                系统状态信息：
                {
                    "success": true,
                    "data": {
                        "face_detection": {...},
                        "gimbal_status": {...},
                        "tracking_status": {...},
                        "system_status": {...},
                        "app_info": {...}
                    }
                }
            """
            try:
                response = await self.http_client.get(f"{self.web_service_url}/api/status")
                response.raise_for_status()
                return response.json()
            except httpx.HTTPError as e:
                logger.error(f"获取系统状态失败: {e}")
                return {"success": False, "error": f"HTTP error occurred: {e}"}
            except Exception as e:
                logger.error(f"获取系统状态异常: {e}")
                return {"success": False, "error": f"An error occurred: {e}"}

    async def run(self):
        """运行MCP服务"""
        try:
            logger.info(f"MCP服务启动: http://{self.config.mcp.host}:{self.config.mcp.port}")

            # 创建服务器任务
            self._server_task = asyncio.create_task(
                self.mcp.run_async(
                    transport="http",
                    host=self.config.mcp.host,
                    port=self.config.mcp.port
                )
            )

            # 等待服务器任务完成或停止事件
            done, pending = await asyncio.wait(
                [self._server_task, asyncio.create_task(self._stop_event.wait())],
                return_when=asyncio.FIRST_COMPLETED
            )

            # 取消未完成的任务
            for task in pending:
                task.cancel()
                try:
                    await task
                except asyncio.CancelledError:
                    pass

        except Exception as e:
            logger.error(f"MCP服务运行错误: {e}")
            raise

    async def stop(self):
        """停止MCP服务"""
        logger.info("正在停止MCP服务...")

        try:
            # 设置停止事件
            self._stop_event.set()

            # 取消服务器任务
            if self._server_task and not self._server_task.done():
                self._server_task.cancel()
                try:
                    await asyncio.wait_for(self._server_task, timeout=3.0)
                except (asyncio.CancelledError, asyncio.TimeoutError):
                    logger.info("MCP服务任务已取消")

            logger.info("MCP服务已停止")

        except Exception as e:
            logger.error(f"停止MCP服务时出错: {e}")
            # 强制停止
            import os
            os._exit(1)
